<?php
include_once LIBRARY_DIRECTORY . 'Model.php';

class GoogleSearch extends Model {
	
	private $_searchRequest = 'http://www.google.com/search?q=[q]&start=[start]';
	const _numberOfGoogleSearchPageToGet = 3;
	
	private $_keyword;
	private $_keywordId;			// retrieve the ID when a keyword gets saved into db
	private $_searchKeyword;
	
	private $_existingKeywod = array();
	private $_existingStopwod = array();
	
	public function __construct($keyword = '')
	{
		// optain this keyword and create the searchable keyword
		$this->_keyword = $keyword;
		$this->_searchKeyword = $this->createWordToSearch($keyword);
		
		// get the array of exsting keywords and existing stop words
		$this->_existingKeywod = $this->getAllKeyword();
		$this->_existingStopwod = $this->getAllStopword();
	}
	
	/**
	 * get all existing keywords from db
	 * 
	 * @return array
	 *
	 */
	public function getAllKeyword()
	{
		$tblName = TBL_INPUT_KEYWORD;
		$q = "SELECT * FROM $tblName";
		$records = $this->fetchMultiple($q);
		
		$keyword = array();
		foreach ($records as $item)
		{
			$keyword[] = $item['input_keyword_word'];
		}
		
		return $keyword;
	}
	
	/**
	 * get all existing stopwords from db
	 * 
	 * @return array
	 *
	 */
	public function getAllStopword()
	{
		$tblName = TBL_STOPWORD;
		$q = "SELECT * FROM $tblName";
		$records = $this->fetchMultiple($q);
		
		$keyword = array();
		foreach ($records as $item)
		{
			$keyword[] = $item['stopword_word'];
		}
		
		return $keyword;
	}
	
	/**
	 * save this keyword in to database
	 *  - process the detailed result of this keyword
	 *
	 * @return 0|1
	 */
	public function saveKeyword()
	{
		$keyword = $this->_keyword;
		
		if (!empty($keyword) && !$this->isDupplicate($keyword))
		{
			// insert this keyword into db - get the last inserted keyword ID
			$tblName = TBL_INPUT_KEYWORD;
			$q = "INSERT INTO $tblName(input_keyword_word) VALUES ('$keyword')";
			$this->_keywordId = $this->insert($q);
			
			// process the detailed results for of this keyword
			$this->processDetailedResult();
			
			return 1;
		}
		
		return 0;
	}
	
	/**
	 * start the search query for this keyword
	 *  - save all links into db
	 *
	 */
	private function processDetailedResult()
	{
		// get the first gogole search result pages
		$googlePage = $this->getGoogleSearchResultPage();
		
		// get links to detailed result pages on each google page
		foreach ($googlePage as $googlePageItem)
		{
			$this->saveLinks($googlePageItem);
		}
	}
	
	/**
	 * save all detailed resultant links of a given google page into db
	 *  - process the content on each link
	 *
	 */
	private function saveLinks($googlePageContent)
	{
		$tblName = TBL_RESULT;
		
		$googlePageContent = strtolower($googlePageContent);
		$inputKeywordId = $this->_keywordId;
		$arrayStopWord = $this->_existingStopwod;
		
		$link = '';
		$iStartFrom = 0;
		do 
		{
			$link = $this->Find_Next($iStartFrom, array('<li class=g>', '<h3 class=r>', '<a href="', '" class=l>'), 2, false, false, $googlePageContent, $iStartFrom);
			if (!empty($link))
			{
				// insert this link into db
				$q = "INSERT INTO $tblName(input_keyword_id, result_link) VALUES($inputKeywordId, '$link')";
				$resultId = $this->insert($q);
				// process this link content
				$this->processLinkContent($resultId, $link, $arrayStopWord);
			}
		}
		while ($link != '');
	}
	
	/**
	 * 
	 *
	 */
	private function processLinkContent($resultId, $link, $arrayStopWord)
	{
		$tblName = TBL_RESULT_WORD;
		
		// get the content of this result link
		$resultContent = @file_get_contents($link);
		$resultContent = strtolower($resultContent);
		// only get the body
		$resultContent = $this->Find_Next(0, array('<body', '>', '</body'), 1, false, false, $resultContent, $iStartFrom);
		// strip out all html entities
		$resultContent = $this->stripHtmlout($resultContent);
		// strip out all signs, only keep a-z in the string
		$resultContent = $this->stripSignsOut($resultContent);
		
		// break the content into an array of words
		$arrayWord = explode(' ', $resultContent);
		$arrayWordToSave = array();
		$arrayFrequencyToSave = array();
		foreach ($arrayWord as $word)
		{
			$word = trim($word);
			if (!empty($word) && !in_array($word, $arrayStopWord))
			{
				if (!in_array($word, $arrayWordToSave))
				{
					$arrayWordToSave[] = $word;
					$arrayFrequencyToSave[] = 1;
				}
				else
				{
					for ($i=0; $i<count($arrayWordToSave); $i++)
						if ($arrayWordToSave[$i] == $word)
						{
							$arrayFrequencyToSave[$i]++;
							break;
						}
				}
			}
		}
		
		// create the query to insert these words into database
		$values = '';
		for ($i=0; $i<count($arrayWordToSave); $i++)
		{
			$word = $arrayWordToSave[$i];
			$frequency = $arrayFrequencyToSave[$i];
			$values .= "($resultId, '$word', $frequency)";
			if ($i < count($arrayWordToSave)-1)
				$values .= ', ';
		}
		if (!empty($values))
		{
			$q = "INSERT INTO $tblName(result_id, result_word_word, result_word_count) VALUES $values";
			$this->insert($q);
		}
	}
	
	private function isDupplicate($keyword)
	{
		$existingKeyword = $this->_existingKeywod;
		if (in_array($keyword, $existingKeyword))
			return true;
			
		return false;
	}
	
	/**
	 * create the suitable input word for google to search
	 *
	 * @param string $keyword
	 * @return string
	 */
	private function createWordToSearch($keyword)
	{
		$keyword = str_replace('+', '%2B', $keyword);
		$keyword = str_replace(' ', '+', $keyword);
		$keyword = str_replace(',', '%2C', $keyword);
		$keyword = str_replace(';', '%3B', $keyword);
		$keyword = str_replace(':', '%3A', $keyword);
		$keyword = str_replace('?', '%3F', $keyword);
		$keyword = str_replace('/', '%2F', $keyword);
		$keyword = str_replace('@', '%40', $keyword);
		$keyword = str_replace('#', '%23', $keyword);
		$keyword = str_replace('$', '%24', $keyword);
		$keyword = str_replace('%', '%25', $keyword);
		$keyword = str_replace('&', '%26', $keyword);
		$keyword = str_replace('=', '%3D', $keyword);
		
		return $keyword;
	}
	
	/**
	 * get the first self::_numberOfGoogleSearchPageToGet pages of the google search result
	 * 
	 * @param string $keyword
	 * @param int $numberOfPage
	 * @return array of self::_numberOfGoogleSearchPageToGet pages
	 */
	private function getGoogleSearchResultPage($numberOfPage = self::_numberOfGoogleSearchPageToGet)
	{
		$searchKeyword = $this->_searchKeyword;
		$googleResultPage = array();
		
		for ($i=0; $i<$numberOfPage; $i++)
		{
			$seachRequest = $this->_searchRequest;
			$seachRequest = str_replace('[q]', $searchKeyword, $seachRequest);
			$seachRequest = str_replace('[start]', $i*10, $seachRequest);
			$googleResultPage[] = @file_get_contents($seachRequest);
		}
		
		return $googleResultPage;
	}
	
	/**
	 * extract the desired data from the given content using the array of containing strings (tokens)
	 *
	 * @param int $iStartFrom
	 * @param array of tokens $sRoot
	 * @param int $iFirst
	 * @param bool $bIncludeFirst
	 * @param bool $bIncludeLast
	 * @param string $sString
	 * @param int& $iNewStartFrom
	 * @return string
	 */
	private function Find_Next($iStartFrom, $sRoot, $iFirst, $bIncludeFirst, $bIncludeLast, $sString, &$iNewStartFrom)
	{
	    $sSubString = substr($sString,$iStartFrom,strlen($sString)-$iStartFrom);
	    $iTime   = count($sRoot);
	    $iStart  = 0;
	    $iEnd    = 0;
	    $pos     = 0;
	    $lastPos = 0;
	
	    for ($i=0; $i<$iTime; $i++)
	    {
	        $pos = strpos($sSubString, $sRoot[$i], $lastPos);
	        if ( $sRoot[$i] != substr($sSubString,0,strlen($sRoot[$i])) )
	            if ($pos==false) return "";
	        $lastPos = $pos + strlen($sRoot[$i]);
	        if ($i==($iTime-1) ) $iEnd=$pos;
	    }
	    if ($bIncludeLast==true)
	        $iEnd = $iEnd+strlen($sRoot[$iTime-1]);
	
	    $pos     = 0;
	    $lastPos = 0;
	    for ($i=0;$i<=$iFirst;$i++)
	    {
	        $pos = strpos($sSubString,$sRoot[$i],$lastPos);
	        $iStart = $pos;
	        $lastPos = $pos+strlen($sRoot[$i])-1;
	    }
	    if ($bIncludeFirst==false)
	        $iStart = $iStart + strlen($sRoot[$iFirst]);
	        
	    $iNewStartFrom = $iStart + $iStartFrom;
	
	    return trim(substr($sSubString,$iStart,$iEnd-$iStart));
	}
    
    private function stripHtmlout($record)
    {
        $record = trim($record);
        //$record = str_replace("'", "\'", $record);
        
        // $document should contain an HTML document.
        // This will remove HTML tags, javascript sections
        // and white space. It will also convert some
        // common HTML entities to their text equivalent.
        $search = array ('@<script[^>]*?>.*?</script>@si', // Strip out javascript
                         '@<[\/\!]*?[^<>]*?>@si',          // Strip out HTML tags
                         '@([\r\n])[\s]+@',                // Strip out white space
                         '@&(quot|#34);@i',                // Replace HTML entities
                         '@&(amp|#38);@i',
                         '@&(lt|#60);@i',
                         '@&(gt|#62);@i',
                         '@&(nbsp|#160);@i',
                         '@&(iexcl|#161);@i',
                         '@&(cent|#162);@i',
                         '@&(pound|#163);@i',
                         '@&(copy|#169);@i',
                         '@&#(\d+);@e');                    // evaluate as php

        $replace = array ('',
                          '',
                          '\1',
                          '"',
                          '&',
                          '<',
                          '>',
                          ' ',
                          chr(161),
                          chr(162),
                          chr(163),
                          chr(169),
                          'chr(\1)');
		
        return preg_replace($search, $replace, $record);
    }
    
    public function stripSignsOut($record)
    {
    	$record = trim($record);
        $record = str_replace('&nbsp;', ' ', $record);
        
        // only key those words containing a-z
        return preg_replace('/[^a-z,\ ]{1}/', '', $record);
    }
	
	public function __destruct()
	{
		
	}

}